[slug].vue 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <!-- @format -->
  2. <script lang="ts" setup>
  3. import dayjs from "dayjs"
  4. import { getBlogsDetailApi, getReleaseBlogApi } from "~/api/model/blogs"
  5. import { ConstKeys } from "~/enums/const-enums"
  6. const route = useRoute()
  7. const detail = ref<any>({})
  8. const list = ref<any>([])
  9. const page_size = ref(3)
  10. const slug = route.params.slug
  11. const { data, pending, error, refresh } = await useAsyncData(
  12. "blog-detail",
  13. () =>
  14. $fetch(`${ConstKeys.DOMAINPRO}/client/content/detail`, { params: { slug } }),
  15. )
  16. const seoData = data.value?.result
  17. useHead({
  18. title: seoData?.contentTitle,
  19. meta: [
  20. {
  21. name: "description",
  22. content: seoData?.contentSubhead,
  23. },
  24. {
  25. property: "og:title",
  26. content: seoData?.contentTitle,
  27. },
  28. {
  29. property: "og:description",
  30. content: seoData?.contentSubhead,
  31. },
  32. {
  33. property: "og:image",
  34. content: seoData?.thumbnailUrl,
  35. },
  36. {
  37. property: 'og:url',
  38. content: `${ConstKeys.DOMAINPRO}/blog/${slug}`,
  39. },
  40. {
  41. property: "og:type",
  42. content: "website",
  43. },
  44. {
  45. property: "twitter:title",
  46. content: seoData?.contentTitle,
  47. },
  48. {
  49. property: "twitter:description",
  50. content: seoData?.contentSubhead,
  51. },
  52. {
  53. property: 'twitter:site',
  54. content: `${ConstKeys.DOMAINPRO}/blog/${slug}`,
  55. },
  56. {
  57. property: "twitter:image",
  58. content: seoData?.thumbnailUrl,
  59. },
  60. {
  61. property: "twitter:card",
  62. content: "summary_large_image",
  63. },
  64. ],
  65. link: [
  66. {
  67. rel: 'canonical',
  68. href: `${ConstKeys.DOMAINPRO}/blog/${slug}`,
  69. },
  70. ],
  71. })
  72. async function getNewBlogsList(
  73. params?: any,
  74. pageNo = 1,
  75. pageSize = page_size.value
  76. ) {
  77. const res: any = await getReleaseBlogApi({
  78. ...params,
  79. type: 1,
  80. pageNo,
  81. pageSize,
  82. })
  83. list.value = res.records
  84. }
  85. getNewBlogsList({
  86. orderBy: "createTime",
  87. orderType: "desc",
  88. })
  89. async function getVideoOrBlogsDetail() {
  90. const res = await getBlogsDetailApi({
  91. slug,
  92. })
  93. detail.value = res
  94. }
  95. getVideoOrBlogsDetail()
  96. </script>
  97. <template>
  98. <div class="blog-detail">
  99. <div class="py-100px pb-120px w-1400px m-auto px-65px">
  100. <h1 class="fw-700 text-40px lh-40px text-#363C40 custom-title-font">
  101. {{ detail.contentTitle }}
  102. </h1>
  103. <div class="mt-20px flex mb-42px text-16px text-#4d4d4d">
  104. <!-- <div class="flex">
  105. <div>Creator:</div>
  106. <div class="ml-10px text-#999999">
  107. {{ detail.createBy }}
  108. </div>
  109. </div> -->
  110. <!-- <div class="mx-10px b-r-1px b-r-solid b-r-#D8D8D8 h-20px" /> -->
  111. <div class="text-16px flex text-#666666">
  112. <div>Last Update: &nbsp;</div>
  113. {{ dayjs(detail?.createTime).format("MM/DD/YYYY") }}
  114. </div>
  115. <div
  116. v-if="detail.category_dictText"
  117. class="mx-10px b-r-1px b-r-solid b-r-#D8D8D8 h-20px"
  118. />
  119. <div class="text-#999999">
  120. {{ detail.category_dictText }}
  121. </div>
  122. </div>
  123. <div
  124. class="text-#333333 content-detail custom-html"
  125. v-html="detail.content"
  126. />
  127. <div class="mt-100px">
  128. <h2 class="fw-700 text-40px text-#3d3d3d !mb-60px custom-title-font">
  129. Here are some related articles you may find interesting:
  130. </h2>
  131. <div class="mt-70px grid grid-cols-3 gap-x-106px gap-y-65px">
  132. <div v-for="(item, index) in list" :key="index">
  133. <common-blog-item :item="item" />
  134. </div>
  135. </div>
  136. </div>
  137. </div>
  138. <AppFooter />
  139. </div>
  140. </template>
  141. <style lang="less" scoped>
  142. .blog-detail {
  143. ::v-deep(.content-detail) {
  144. font-family: sans-serif;
  145. h2 {
  146. font-size: 1.5em;
  147. font-family: "CustomTitleFont";
  148. margin-top: 1em !important;
  149. margin-bottom: 1em !important;
  150. font-weight: bold;
  151. }
  152. h3 {
  153. display: block;
  154. font-size: 1.17em;
  155. margin-block-start: 1em;
  156. margin-block-end: 1em;
  157. margin-inline-start: 0px;
  158. margin-inline-end: 0px;
  159. margin-bottom: 1em !important;
  160. font-weight: bold;
  161. unicode-bidi: isolate;
  162. font-family: "CustomTitleFont";
  163. }
  164. p {
  165. display: block;
  166. margin-block-start: 1em;
  167. margin-block-end: 1em;
  168. margin-inline-start: 0px;
  169. margin-inline-end: 0px;
  170. unicode-bidi: isolate;
  171. }
  172. ul {
  173. display: block;
  174. list-style-type: disc;
  175. margin-block-start: 1em;
  176. margin-block-end: 1em;
  177. margin-inline-start: 0px;
  178. margin-inline-end: 0px;
  179. padding-inline-start: 40px;
  180. unicode-bidi: isolate;
  181. li {
  182. display: list-item;
  183. text-align: -webkit-match-parent;
  184. unicode-bidi: isolate;
  185. }
  186. }
  187. ol {
  188. list-style-type: decimal;
  189. display: block;
  190. list-style-type: decimal;
  191. margin-block-start: 1em;
  192. margin-block-end: 1em;
  193. margin-inline-start: 0px;
  194. margin-inline-end: 0px;
  195. padding-inline-start: 40px;
  196. unicode-bidi: isolate;
  197. }
  198. }
  199. }
  200. </style>